Fuzzy BASIC ――――――――――――――――――――――――――――――――――――――― Oh!mz 1986年 9月号掲載 S−OS SWORD用 Fuzzy BASIC マシン語 Fuzzy BASIC.SOS  03000H−057FFH S:03000H サンプル1 チェックサムプログラム BASIC  Check sum.BAS 改造ZEDA用ソース  Fuzzy1.ASC  Fuzzy2.ASC  Fuzzy3.ASC -------------------------------------- 改造ZEDA マシン語  Kazizou ZEDA.SOS  03000H−04DFFH S:03000H パッチ プログラム (要ZEDA)  Kazizou ZEDA.PAT  04D00H−04E2FH S:04D00H パッチ プログラム ソース (ZEDA用)  Kazizou ZEDA.ASC 起動方法 Fuzzy BASIC.SOSをロード J3000 コールドスタート:03000H ホットスタート :03003H *Fuzzy1.ASCの  1477の…”Foundを…”Found ”に修正 ――――――――――――――――――――――――――――――――――――――― ついに登場、S−OS用のBASICインタプリタです。 マシン語・システムに近い命令から 再帰・構造化などたいへんユニークな機能を備えて乙ますし、 命令の拡張も可能になっています。 このBASICをアセンブルするために ZEDAにリンク機能を付け、 おまけに高速化をするプログラムも紹介します。 おおいに活用してください。 MZ/X1で使える整数型BASICといいますと、 市販されているものとしてはWICS、 dB−IBASICなどがありますが、 Oh!MZの読者の方でしたら、以前誌上で発表されました T.T.L.インタプリタを使っている方も多いのではないでしようか。 T.T.L.は一種の記号化された整数型BASIC(?)で Tiny言語の流れをくむものです。 ワークエリアを含めて8Kバイトとコンパクトながら 基本的なBASICの機能はすべて持ち、 サブルーチン内で局所変数が使え、 メモリやI/Oの操作も強力という優れたものでした。 Fuzzy BASICはWICSインタプリタから グラフィツク処理を除き、T.T.L.の長所を取り入れ、 少々の命令を加えたものといえます。 その少々の命令には、構造化したプログラムを書くのに必要なもの、 逆にZ80の機械語に近いもの、“SWORD”のおかげで 付け加えられたファイル処理関係ステートメントなどがあります。 さらに、ユーザーがステートメントを追加することもできますので、 力のある方ならより強力なものにすることも可能でしよう。 ・――――             ・ |      入力方法       | ・             ――――・ ダンプで打ち込む方は各機種のマシン語モニタ または入力ツールを使って 3000H〜57FFHまで入力してください。 ソースの場合はZEDAではアセンブルできませんので リスト1の改造版ZEDAでアセンブルしてください。 これは従来のZEDAに 分割アセンブル機能とハッシュ表を追加したものです。 これにより大規模なプログラム開発と 最低5倍の高速アセンブルが可能になりました。 なお、改造版ZEDAの内容やアセンブル手順については 表1、2を参照してください。 ●表1 改造版ZEDA追加コマンド ・―――――――――――――――――――――――――――・ |                           | | A0      ハッシュテーブルをクリアする     | | A1      Pass1のみ行う          | |        (ラベルテーブルの作成)       | | A2〔/〔/〕〕 Pass2のみ行う          | |        (すでに作られているラベルテーブルを | |         使って、オブジェクトを生成する)  | |                           | |                           | | 分割アセンブルの手順                | | 1)A0を実行する                  | | 2)テキストをロードする              | | 3)A1を実行する                  | | 4)すべてのテキストについて、2〜3を繰り返す   | | 5)テキストをロードする              | | 6)A2を実行する                  | | 7)必要に応じてオブジェクトをセーブしておく    | | 8)すべてのテキストについて、5〜7を繰り返す   | |  (A1のときと同じ順序であること)         | | 9)S−OSのモニタに戻る             | | 10)7)でセーブしたオブジェクトを適当な順序で   | |   すべてロードする                | | 11)必要な範囲をセーブする             | |                           | |コールドスタートは4D00Hであるが、          | |1回起動したあとに、3000H〜4DFFHをセーブしておけば、| |次回からは3000Hヘジャンプすればコールドスタートする。| |テキストエリアは4E00Hからである           | ・―――――――――――――――――――――――――――・ ●表2 Fuzzy BASICアセンブル手順 ・―――――――――――――――――――――――――――・ |                           | | 1)A0を実行する                  | | 2)エディタヘ戻ってソース1をロードする      | | 3)アセンブラモードにしたのち、A1を実行する    | | 4)ソース2をロードする              | | 5)A1を実行する                  | | 6)ソース3をロードする              | | 7)A1を実行する                  | | 8)ソース1をロードする              | | 9)A2を実行する                  | | 10)3000H〜3ABCHの範囲をセーブする        | |   ただし4E00H〜3000Hのオフセットが       | |   付いているので。                | |   S3000 3ABC 0000 4E00:Fuzzy1.$$$         | |   としてセーブする。               | |   以下の場合も同様                | | 11)ソース2をロードし、A2を実行する        | | 12)3ABDH〜57FFHの範囲をセーブする        | |   (ここではFuzzy2.$$$の名とする)        | | 13)ソース3をロードし、A2を実行する        | | 14)515AH〜57D5Hの範囲をセーブする        | |   (Fuzzy3.$$$の名としておく)          | | 15)!でS−OSへ戻る               | | 16)Fuzzy1.$$$、Fuzzy2.$$$、Fuzzy3.$$$の順に    | |   ロードする                   | | 17)新たに3000H〜57FFHの範囲をファイル名を付けて | |   セーブする                   | | 18)もう必要ないので、Fuzzy1.$$$、Fuzzy2.$$$、   | |   Fuzzy3.$$$を消去してしまって良い        | |                           | ・―――――――――――――――――――――――――――・ ・――――             ・ |      命令の特徴      | ・             ――――・ おのおのの命令についてはマニュアルを見ていただくことにして、 以下にわかりにくい点や細かな注意について 思いつくままに書いてみたいと思います。 ●メモリ配列とI/O配列 WICSでいう配列と T.T.L.のメモリ変数・I/O変数のあいの子と思えば 間違いないでしょう。 機械語のインデックスアドレッシングに相当します。 これによりメモリ(とI/O)の特定のアドレスを 一般変数とほとんど区別することなく扱うことができます。 ●ラベル 最近のBASICでは標準装備になっていますので 多くを述べる必要はないでしよう。 GOTO文などの分岐先として、 行番号の代わりに用いられます。 各サブルーチンの頭にそのルーチンの機能を ひと言で表すような単語をラベルとして記述することで プログラムの読みやすさを向上させるわけです。 内部処理に関して付け加えます。 ラベルが使われると該当する行をテキストの最初から順に探していきます。 よってラベルが二重に定義された場合は先に出てきたほうが優先されます。 同じ理由でラベルを使うとプログラムの実行速度は低下します。 ですがスピードよりも読みやすさに重点を置く場合には、 ラベルの使用は有効でしょう。 また、GOTO文などの分岐先として式の使用を許したために、 RENUMコマンド によって行番号を付け換えても 分岐先行番号の変更はしないので、ここでもラベルが有用なものとなります。 開発中はラベルを用いるようにし、 行番号をきれいに整理し終わった時点でスピードが必要とされる部分を ラベルから行番号に直すようにすればよいでしょう。 このときSEARCHコマンドを使えば少ない労力ですみます。 ●PROC/RETPROC 変数の退避をともなうGOSUB文 といえます。 T.T.L.の:=文とほとんど同じですが、 局所変数をLOCAL文 によって変更できる点が違います。 PROCが実行されると、LOCAL文 で指定された6つの変数を 変数スタックにプッシュしてからサブルーチンを呼び出します。 このとき、カンマで区切った6つまでの式があると、 その値を局所変数に与えてからサブルーチンを呼びます。 そして、RETPROC により 先にプッシュした6つの変数の値を復帰させてリターンします。 つまりサブルーチンの中で局所変数のみを使う限り、 メインルーチンと変数の重複を気にせずに プログラムが書けるわけです。 ●FUNC/RETFUNC PROC文を関数にしたものです。 変数の退避を行ってから サブルーチンを呼び出すことに変わりありませんが、 関数ですから式の中でのみ用いられ値を持つことになります。 サブルーチンでRETFUNC を見つけると、 指定された値を持ってリターンします。 参考としてPROC、FUNCを使った サンプルプログラムを載せておきます。 ●変数スタック 変数スタックはユーザーが任意にデータを保存しておくほかに PROC文、FUNC関数の変数退避に用いられ、 1回のPROC文およびFUNC関数の実行ごとに12バイトが使われます。 また、FUNC関数はこのほかに CPUのスタックを十数〜数十バイト(式の複雑さによる)を使います。 変数スタックとCPUスタックは、 5D00H〜6300Hまでの範囲に連続して置かれており、 VSTACKコマンドによって、その境界を変更することができます。 また、変数スタックをLIMIT で確保したアドレスなど、 まったくほかのアドレスに移動させれば、 5D00H〜6300HのすべてをCPUスタックとして使用できますので、 深く再帰するFUNC関数も実行できるようになります。 ●ループ文 FOR〜NEXT のほかにREPEAT〜UNTIL とWHILE〜WEND があります。 場合によって使い分けてみてください。 処理速度の点ではFORループ がもっとも速く、 REPEAT、WHILE の順になります。 ときに、ループを途中で抜けたい場合があるでしょうが、 単にGOTOで分岐してしまうとスタックが積まれたままになってしまうので、 分岐先でNEXTまたはUNTIL を実行しなければなりません。 FORループ ではよく使うテクニックだと思います。 また、ブロックIF文からの脱出にも同じ方法が使えます。 WHILEループ だけは、WENDが見つかった時点で 必ずWHILE に戻ってしまいますので この方法は使えません。 ●STON/STOFF STOP文の有効/無効を決めるものです。 デバッグ時に入れたSTOP文を残したままで プログラムを実行できます。 ●BRON/BROFF たとえばバンク切り換えをしてG−RAMをアクセスするときに、 途中でBREAKしたりすると困ったことになります。 そんな場合はG−RAMアクセスルーチンの直前にBROFF文 を、 直後にBRON文を入れるようにして 途中でプログラムが中断しないようにします。 また、FUNC関数ルーチン内でプログラムの実行を中断すると、 CONTによる再開がうまくできなくなりますので、 前後にこの文を置く必要が出てくるでしょう。 ●LDIR/LDDR/TRANS メモリのブロック転送をする命令です。 今までのBASICでは、ブロック転送したい場合 ループを組んでやらなければならなかったのですが、 これらのステートメントを使えば一発です。 メモリ上にVRAMが置かれている機種なら、 スクロールなどにも利用できるでしょう。 LDIR文、LDDR文は名前も機能もZ80の命令と同じですので わかりやすいと思います。 メモリのクリアやフルにも有効なものです。 TRANS文 は、データと転送先のアドレスにより、 LDIRとLDDRを使い分けています。 ●CODE/TABLE CODE関数は予約語名を与えると中間コードを返す関数、 TABLE関数 は逆に中間コードから 予約語名を(ポインタとして)返す関数です。 数あるステートメント、関数の中でも もっともえたいの知れないものでしょう。 実は私も何に使うか考えないで作ったのですが ここでヒントを2つ挙げておきましょう。 ひとつはBASICコンパイラを書こうとするときに たぶん使えるでしょう。 もうひとつは、上のこととも重複しますが、 BASICのテキストに対してなんらかの処理を行う場合です。 たとえばファイルコンバータや、 アスキーセーブなんかが考えられます。 このBASICではメモリ上に複数のテキストを置くこともできますから、 ほかのアドレスに置かれたテキストに対して 処理を行うようなプログラムが作れるのです。 CODE関数を使う上で注意する点として、 関数は最初のカッコまでが予約語となっているということです。  (誤)A=CODE(FUNC)  (正)A=CODE(FUNC( ) ・――――             ・ |  命令・関数の拡張について   | ・             ――――・ Fuzzy BASICでは ステートメントなどの追加ができるようになっています。 追加は機械語サブルーチンを作り その先頭アドレスをジャンプテーブルに登録することによって行います。 ステートメントは、USR^A、……、USR^Hの名前で8つまでが追加可能です。 ジャンプテーブルは5092Hからの2バイトずつが対応します。 関数は、FN^A、……、FN^Hの名前で8つまで追加できます。 ジャンプテーブルは511FHからです。 またPRINT文用出力関数 もPR^AとPR^Bの2つが追加でき、 ジャンプテーブルは5156Hから2バイトずつ4バイトです。 ジャンプテーブルに登録する際は、 アドレスを下位、上位の順に登録するのを忘れないでください。 以下にサブルーチンを作る上で必要と思われることを述べます。 テキストの実行ポインタとしてIXレジスタが使われています。 プログラムが実行されるときは、 IXレジスタを順にインクリメントしてテキストが読み込まれ、 解析されることになります。 各ステートメントの機械語サブルーチンが呼び出された時点で、 IXレジスタはそのステートメントの中間コードが置かれた 直後のアドレスを指しています。 サブルーチン内では汎用レジスタはすべて自由に使えます。 IXレジスタは先に述べたように テキスト実行ポインタとして使われていますから、 勝手な使い方はできません。 また、IYレジスタはFORループ用 のスタックポインタとして 使われているので保存する必要があります。 サブルーチンからはRET によって戻り、 その時点でIXレジスタは シングルクォーテーション(’)かコロン(:)か0DH(行のエンドコード) のある アドレスを指していなければなりません。 関数はリターン時のHLレジスタが値となりますので、 演算結果を入れてリターンするようにします。 表3にサブルーチンを組むのに必要と思われる インタプリタ内のルーチンのエントリーアドレスを示しておきます。 ほかにも使えるものはあると思いますので、 ソースリストも参考にしてください。 インタプリタ内にはほとんど空きがないので、 新しく作ったサブルーチンはまったく別にセーブしておくか、 次に述べる方法によりインタプリタに組み込みます。 まずサブルーチンを6300Hからの本来なら テキストが格納されるべきアドレスに置くようにします。 そして307EH、307FHの2バイト(00H、63Hになっている)を サブルーチンの終わった直後のアドレスの上位、下位を 逆転したものに変更します。 最後に3000Hから新しいルーチンの最後までを セーブすれば完成です。 ●図1 メモリマップ ・――――――――――――――――――――――・ | $3000 ・―――――――――――――――・ | |    |SF-BASICインタプリタ本体 | | |    | | | |     〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜  | |     〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜  | |    | | | | 57D6H |―――――――――――――――| | |    |変数名エリア (203バイト)| | | 58A1H |―――――――――――――――| | |    |変数値エリア (254バイト)| | | 599FH |―――――――――――――――| | |    |各種ワーク (54バイト)| | | 59DCH |―――――――――――――――| | |    |ブロックIFスタック (16バイト)| | | 59ECH |―――――――――――――――| | |    |REPEAT〜UNTILスタック (32バイト)| | | 5A0CH |―――――――――――――――| | |    |WHILE〜WENDスタック (32バイト)| | | 5A2CH |―――――――――――――――| | |    |FOR〜NEXTスタック | | |    | (128バイト)| | | 5AACH |―――――――――――――――| | |    |GOSUB,PROC,FUNCスタック | | |    | (254バイト)| | | 5BAAH |―――――――――――――――| | |    |GOTOなど用行番号←→アドレステーブル| | |    | (254バイト)| | | 5CA8H |―――――――――――――――| | |    |中間コードヘの変換用バッファ | | |    | (88バイト)| | | 5D00H |―――――――――――――――| | |    |変数スタック | | |    |               | | |    |               | | |(6200H) |- - - - - - - - - - - - - - - | | |    |マシンスタック | | |    |               | | | 6300H |―――――――――――――――| | |    |TEXTエリア | | |    |               | | |    ・―――――――――――――――・ | ・――――――――――――――――――――――・ ●表3 新命令語追加用インタプリタ内ルーチン ・――――――――――――――――――――――・ |3000H コールドスタート | |3003H ホットスタート | |3006H IXレジスタの指すアドレスからの1行の式を | | 評価し、HLに入れて戻る。 | | リターン時、IXレジスタは式の直後のアドレスを | | 指す。 | |3009H カンマで区切られた2つの式(式,式の形)を | | 評価し、順にHL,DEレジスタに入れて戻る。 | |300CH カンマで区切られた3つの式を評価し、 | | 順にHL、DE,BCレジスタに入れて戻る。 | |300FH 式の評価のあと閉じカッコがなければエラーを | | 発生する。 | | 値はHLレジスタに持ってリターンすることは | | 上と同様。 | |3012H 2つの式の評価と閉じカッコのチェックを | | 続けて行う。 | |3015H 3つの式の評価と閉じカッコのチェックを続けて | | 行う。 | |3018H シンタックスエラー(エラー13)を発生する。 | |301BH Bad Dataエラー(エラー14)を発生する。 | |301EH アキュムレータの値に対応するエラーを発生する。 | |3021H IXレジスタの指すアドレスからの変数名に対応 | | して、DEレジスタに変数値、HLレジスタに | | その格納アドレスを持ってリターンする。 | ・――――――――――――――――――――――・ ・――――             ・ |       最後に       | ・             ――――・ インタプリタ本体はワークを除いても 約10KバイトとというS−OS関係のものとしては大きなものです。 なんといってもBASICという言語の性格上、 エディタからDOSもどきまでを含んでいるのですから。 システムプログラムを作るのは初めてだったものですから、 速度とメモリ効率との兼ね合いもいまひとっだったかもしれません。 最初はコンパイラをも作ることを前提とし、 また、サブルーチン単位でのコンパイルを可能とするため ランタイムパッケージをインタプリタ内に持ち、 かつ分離もできるようにするつもりでしたが、 思っていた以上に命令語が増え、 結果としてオブェクトサイズがまりにも巨大になってしまったので、 とうとう純粋なインタプリタとなってしまいました。 ただし、まだまだコンパイラの夢は捨てていませんよ。 スピードが気になる方のために 簡単なベンチマークテストの結果を付け加えておきます。 本来ならベンチマークプログラム自体も掲載するべきでしょうが、 「えこひいき」していないことだけを申し添えておきます。 機種はMZ-2200上 で行いました。 S−OSをかんでいるので、機種別に多少の違いが出ると思いますし、 クロックが違えばさらに異なった結果が出ますが、 参考程度にはなるでしょう。 これまでのBASICとはひと味違ったものに仕上げたつもりですので、 ひと味違った使い方をしてください。 来月はこのBASICのユニークな命令を活用するための サンプルを紹介する予定です。 お楽しみに。 〈参考文献〉 MZ-2200 BASIC/MONITOR MANUAL、シャープ MZ-2200 OWNER'S MANUAL、シャープ HuBASIC ver2.0 reference book、ハドソンソフト YOU&美香:T.T.L.インタプリタ、Oh!MZ、1984、10、pp.81-108 BASIC行動学入門、Oh!MZ、1986、1、pp.41-113 その他S−OS関連記事、Oh!MZ ●表4 ベンチマークテスト結果(MZ-2200上 単位は秒) ・――――――――――――――――――――――――――――――――・ |        |Fuzzy BASIC |  WICS | T.T.L. |MZ-1Z001 | |        |      |(インタープリタ)|    |     | |――――――――+――――――+―――――+――――+―――――| |5万回素ループ |   15  | ――  |  38 |  30  | |3万回素ループ |   9  |  9  |  22 |  17  | |3万回GOSUB(1) |   32  |  32  |  69 |  57  | |   同  (2) |   32  |  32  |  228 |  230  | |3万回加算   |   31  |  35  |  44 |  55  | |   減算   |   31  |  35  |  45 |  55  | |   乗算   |   38  |  36  |  52 |  63  | |   除算   |   42  |  46  |  56 |  72  | |1万回文字表示 |   15  |  21  |  19 |  33  | ・――――――――――――――――――――――――――――――――・  ・テストプログラムはOh!MZ’85年11月号のものとほぼ同じ。  ・時間の計測には、WICSはTIME関数、MZ-1Z001ではTI$、   Fuzzy BASICとT.T.L.ではモニタMZ-1Z001M内のサブルーチンを利用。  ・3万回GOSUB(2)は、(1)のプログラムの前に100行のREMを入れたもの。   なお、ループの時間は引いていない。